﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.Threading;

namespace SkypeTranslater
{
	/// <summary>
	/// Interaction logic for MainWindow.xaml
	/// </summary>
	public partial class MainWindow
	{
		private const string c_separator = "----------------------------------------";

		private System.Timers.Timer timer = null;
		private System.Timers.Timer m_timer = null;

		private Thread m_monitorThread = null;
		private CancellationTokenSource m_cts = null;
		private SKYPE4COMLib.Call m_currentCall = null;

		/// <summary>
		/// Collection of users and language groups, which needs to be translated
		/// </summary>
		private GroupCollection m_collection = null;
		private Outlook.OutlookItemCollection m_outlookCol = null;

		private SKYPE4COMLib.Call call = null;

		/// <summary>
		/// Reference to googleTranslate Api
		/// </summary>
		private Translator.GoogleTranslateApi.GoogleTranslateApiImplementation m_translateModule = null;

		private OutlookManagerLibrary.OutlookManager m_outlookMan = null;
		private bool m_isAttachedToSkype = false;

		/// <summary>
		/// Perform invoking of settings window
		/// </summary>
		public static RoutedCommand SettingsCommand = new RoutedCommand();

		/// <summary>
		/// Perform invoking of mail listening command
		/// </summary>
		public static RoutedCommand MailListCommand = new RoutedCommand();

		public static RoutedCommand QuickTranslateCommand = new RoutedCommand();

		public MainWindow()
		{
			InitializeComponent();
			InitializeCommands();

			m_outlookCol = new Outlook.OutlookItemCollection();
			Elysium.Theme.ThemeManager.Instance.Light(Elysium.Theme.ThemeManager.Instance.Accent);

			this.Loaded += new RoutedEventHandler(MainWindow_Loaded);
			this.Closing += new System.ComponentModel.CancelEventHandler(MainWindow_Closing);

			//additional timer for attaching to skype
			m_timer = new System.Timers.Timer(TimeSpan.FromSeconds(6).TotalMilliseconds);
			m_timer.Elapsed += new System.Timers.ElapsedEventHandler(m_timer_Elapsed);
			m_timer.Start();

			m_cts = new CancellationTokenSource();
			m_monitorThread = new Thread(new ThreadStart(MonitorCallAndRefreshCamera));
		}

		private void MonitorCallAndRefreshCamera()
		{
			Thread.Sleep(TimeSpan.FromSeconds(2));

			while (true)
			{
				if (m_cts.IsCancellationRequested)
					break;

				if (App.Skype.Client.IsRunning && m_isAttachedToSkype)
				{
					if (m_currentCall != null
						&& m_currentCall.Status == SKYPE4COMLib.TCallStatus.clsInProgress
						&& m_currentCall.Participants.Count >= 1
						&& m_currentCall.PartnerDisplayName.Equals(SkypeTranslater.Properties.Resources.restarCamParticipant, StringComparison.OrdinalIgnoreCase)
						)
					{
						if (m_currentCall.VideoSendStatus == SKYPE4COMLib.TCallVideoSendStatus.vssRunning)
						{
							m_currentCall.StopVideoSend();
							Thread.Sleep(TimeSpan.FromMilliseconds(600));
							m_currentCall.StartVideoSend();
						}
					}
				}

				if (m_cts.IsCancellationRequested)
					break;

				Thread.Sleep(TimeSpan.FromSeconds(10));
			}
		}

		void m_timer_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
		{
			if (App.Skype.Client.IsRunning && m_isAttachedToSkype)
				return;

			if (App.Skype.Client.IsRunning && !m_isAttachedToSkype)
			{
				var runSkype = new System.Threading.Tasks.Task(new Action(() =>
					{
						App.Skype.Attach(7);

						m_isAttachedToSkype = true;
					}));
				runSkype.Start();
				runSkype.ContinueWith(prevTask =>
				{
					App.Log.Error(SkypeTranslater.Properties.Resources.errorAttachingSkype, prevTask.Exception);
					m_isAttachedToSkype = false;
					runSkype.Dispose();
				}, System.Threading.Tasks.TaskContinuationOptions.NotOnRanToCompletion);
			}

		}

		#region commands initialization
		private void InitializeCommands()
		{
			this.CommandBindings.Add(new CommandBinding(ApplicationCommands.Close, CloseExecuted, CloseCanExecute));

			this.CommandBindings.Add(new CommandBinding(SettingsCommand, SettingsExecuted, CanSettingsExecute));
			this.InputBindings.Add(new InputBinding(SettingsCommand, new KeyGesture(Key.S, ModifierKeys.Control)));

			this.CommandBindings.Add(new CommandBinding(MailListCommand, MailExecuted, CanMailExecute));
			this.InputBindings.Add(new InputBinding(MailListCommand, new KeyGesture(Key.M, ModifierKeys.Control)));

			cbSettings.Command = SettingsCommand;

			this.CommandBindings.Add(new CommandBinding(QuickTranslateCommand, (s, e) =>
			{
				e.Handled = true;
				if (appBar.IsOpen)
					appBar.IsOpen = false;

				Windows.QuickTranslateWindow quick = new Windows.QuickTranslateWindow();
				quick.Owner = this;
				quick.ShowInTaskbar = false;
				quick.ShowDialog();
			}, (s, e) => e.CanExecute = this.OwnedWindows.Count == 0));
			this.InputBindings.Add(new InputBinding(QuickTranslateCommand, new KeyGesture(Key.Q, ModifierKeys.Control)));

			cbQuickTrans.Command = QuickTranslateCommand;
		}

		void CloseExecuted(object sender, ExecutedRoutedEventArgs e)
		{
			appBar.IsOpen = false;
			e.Handled = true;
			Application.Current.Shutdown();
		}

		void CloseCanExecute(object sender, CanExecuteRoutedEventArgs e)
		{
			e.CanExecute = true;
		}

		void SettingsExecuted(object sender, ExecutedRoutedEventArgs e)
		{
			e.Handled = true;
			if (appBar.IsOpen)
				appBar.IsOpen = false;

			SettingsWindow settings = new SettingsWindow(m_collection.ImportToList());
			settings.Owner = this;
			if (settings.ShowDialog() == true)
			{
				m_collection.SaveToXml(m_collection.OpenedFile);
			}
		}

		void CanSettingsExecute(object sender, CanExecuteRoutedEventArgs e)
		{
			e.CanExecute = true;
		}

		void MailExecuted(object sender, ExecutedRoutedEventArgs e)
		{
			e.Handled = true;
			if (appBar.IsOpen)
				appBar.IsOpen = false;

			System.Threading.Tasks.Task<List<OutlookManagerLibrary.OutlookMailInfo>> mailtask =
				new System.Threading.Tasks.Task<List<OutlookManagerLibrary.OutlookMailInfo>>(new Func<List<OutlookManagerLibrary.OutlookMailInfo>>(() =>
				{
					List<OutlookManagerLibrary.OutlookMailInfo> mails = m_outlookMan.GetEmailListFromFolder("DICOM");
					return mails;
				}), System.Threading.Tasks.TaskCreationOptions.LongRunning);

			mailtask.Start();

			mailtask.ContinueWith(prevTask =>
				{
					Windows.MailWindow mailWind = new Windows.MailWindow();
					mailWind.ShowInTaskbar = false;
					mailWind.Owner = this;
					mailWind.ShowActivated = true;
					mailWind.WindowStartupLocation = System.Windows.WindowStartupLocation.CenterOwner;

					WeakReference refWeak = new WeakReference(prevTask.Result);
					mailWind.MailItems = refWeak.Target as List<OutlookManagerLibrary.OutlookMailInfo>;

					mailWind.UnreadCount = m_outlookMan.GetUnreadCountFromFolder("DICOM");
					mailWind.Folders = m_outlookMan.FoldersInOutlook;

					mailWind.InitializeOutlookManager(m_outlookMan);
					mailWind.ShowDialog();
					prevTask.Dispose();

				}, System.Threading.CancellationToken.None, System.Threading.Tasks.TaskContinuationOptions.OnlyOnRanToCompletion
				, System.Threading.Tasks.TaskScheduler.FromCurrentSynchronizationContext());

			mailtask.ContinueWith(prevTask =>
				{
					App.Log.Error(SkypeTranslater.Properties.Resources.errorMailListGet, prevTask.Exception);
					prevTask.Dispose();
				}, System.Threading.Tasks.TaskContinuationOptions.NotOnRanToCompletion);

		}

		void CanMailExecute(object sender, CanExecuteRoutedEventArgs e)
		{
			e.CanExecute = true;
		}
		#endregion

		internal void SetColection(GroupCollection collection)
		{
			if (collection == null)
				throw new ArgumentNullException("collection");

			m_collection = collection;
		}

		internal void SetColectionOutlook(Outlook.OutlookItemCollection collection)
		{
			if (collection == null)
				throw new ArgumentNullException("collection");

			m_outlookCol = collection;
		}

		void MainWindow_Loaded(object sender, RoutedEventArgs e)
		{
			if (App.TranslateOutlook && OutlookManagerLibrary.OutlookManager.IsOutlookInstalled())
			{
				var initOutlookTask = new System.Threading.Tasks.Task(new Action(() =>
					{
						m_outlookMan = new OutlookManagerLibrary.OutlookManager();
						m_outlookMan.MailReceived += new EventHandler<OutlookManagerLibrary.NewMailReceivedEventArgs>(m_outlookMan_MailReceived);
						m_outlookMan.SetFoldersToMonitor(System.Configuration.ConfigurationManager.AppSettings["MAPIFolders"].Split(
																								new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries));
					}));
				initOutlookTask.Start();

				initOutlookTask.ContinueWith(initTask =>
					{
						App.Log.Error("Error occurred while initialize OutlookManager", initTask.Exception);
						initOutlookTask.Dispose();
						if (m_outlookMan != null) m_outlookMan = null;
					}, System.Threading.Tasks.TaskContinuationOptions.NotOnRanToCompletion);

				initOutlookTask.ContinueWith(initTask =>
					{
						App.Log.Info("OutlookManager initialized successfully");
						initOutlookTask.Dispose();
					}, System.Threading.Tasks.TaskContinuationOptions.OnlyOnRanToCompletion);
			}

			m_translateModule = new Translator.GoogleTranslateApi.GoogleTranslateApiImplementation();

			App.Skype.MessageStatus += new SKYPE4COMLib._ISkypeEvents_MessageStatusEventHandler(Skype_MessageStatus);
			App.Skype.CallStatus += new SKYPE4COMLib._ISkypeEvents_CallStatusEventHandler(Skype_CallStatus);
			App.Skype.ApplicationConnecting += new SKYPE4COMLib._ISkypeEvents_ApplicationConnectingEventHandler(Skype_ApplicationConnecting);
			App.Skype.Error += new SKYPE4COMLib._ISkypeEvents_ErrorEventHandler(Skype_Error);

			m_monitorThread.Start();

			//SKYPE4COMLib._ISkypeEvents events = (SKYPE4COMLib._ISkypeEvents)App.Skype;
			//if (events != null)
			//{
			//    events.AttachmentStatus(SKYPE4COMLib.TAttachmentStatus.apiAttachSuccess);
			//}
		}

		void MainWindow_Closing(object sender, System.ComponentModel.CancelEventArgs e)
		{
			e.Cancel = false;

			m_cts.Cancel();
			m_cts.Dispose();

			m_timer.Stop();
			m_timer.Dispose();

			if (m_outlookMan != null)
			{
				m_outlookMan.MailReceived -= new EventHandler<OutlookManagerLibrary.NewMailReceivedEventArgs>(m_outlookMan_MailReceived);
				m_outlookMan.Dispose();
			}

			App.Skype.MessageStatus -= new SKYPE4COMLib._ISkypeEvents_MessageStatusEventHandler(Skype_MessageStatus);
			App.Skype.CallStatus -= new SKYPE4COMLib._ISkypeEvents_CallStatusEventHandler(Skype_CallStatus);
			App.Skype.ApplicationConnecting -= new SKYPE4COMLib._ISkypeEvents_ApplicationConnectingEventHandler(Skype_ApplicationConnecting);
			App.Skype.Error -= new SKYPE4COMLib._ISkypeEvents_ErrorEventHandler(Skype_Error);
		}

		void m_outlookMan_MailReceived(object sender, OutlookManagerLibrary.NewMailReceivedEventArgs e)
		{
			System.Threading.Tasks.Task<string> translateTask = new System.Threading.Tasks.Task<string>(new Func<string>(() =>
				{
					using (TranslateServiceReference.TranslateServiceClient client = new TranslateServiceReference.TranslateServiceClient())
					{
						var response = client.Translate(new TranslateServiceReference.TranslationRequest() { FromLanguage = "en", ToLanguage = "ru", Content = e.Body });
						return response.TranslationResult;
					}
				}));

			translateTask.Start();

			translateTask.ContinueWith(prevTask =>
			{
				MessageBox.Show(prevTask.Result);
				translateTask.Dispose();
			}, System.Threading.CancellationToken.None,
			   System.Threading.Tasks.TaskContinuationOptions.OnlyOnRanToCompletion,
			   System.Threading.Tasks.TaskScheduler.FromCurrentSynchronizationContext());

			translateTask.ContinueWith(prevTask =>
			{
				App.Log.Error("Error occurred while try to translate message in wcf service", prevTask.Exception);
				translateTask.Dispose();
			}, System.Threading.Tasks.TaskContinuationOptions.NotOnRanToCompletion);
		}

		private TextBox CreateNewTab(string tabHeader)
		{
			TextBox tb = null;

			if (mainTabCtrl.CheckAccess())
			{
				TabItem item = new TabItem();
				item.Header = tabHeader;
				tb = new TextBox() { AcceptsReturn = true, AcceptsTab = true, IsReadOnly = true };
				item.Content = tb;

				mainTabCtrl.BeginInit();
				mainTabCtrl.Items.Add(item);
				mainTabCtrl.SelectedItem = item;
				mainTabCtrl.EndInit();
			}
			else
			{
				mainTabCtrl.Dispatcher.Invoke(new Action(() =>
					{
						TabItem item = new TabItem();
						item.Header = tabHeader;
						tb = new TextBox() { AcceptsReturn = true, AcceptsTab = true, IsReadOnly = true };
						item.Content = tb;

						mainTabCtrl.BeginInit();
						mainTabCtrl.Items.Add(item);
						mainTabCtrl.SelectedItem = item;
						mainTabCtrl.EndInit();
					}), System.Windows.Threading.DispatcherPriority.Normal);
			}
			return tb;
		}

		void Skype_Error(SKYPE4COMLib.Command pCommand, int Number, string Description)
		{
			string message = string.Format("Error!  {0} Expected {1}  Number: {2}  Description:{3}", Environment.NewLine, pCommand.Expected, Number, Description);
			App.Log.Error(message);
			MessageBox.Show(this, message);
		}

		void Skype_ApplicationConnecting(SKYPE4COMLib.Application pApp, SKYPE4COMLib.UserCollection pUsers)
		{
			string message = string.Format("Application {0}, users count {1}", pApp.Name, pUsers.Count);
			MessageBox.Show(this, message);
		}

		void Skype_CallStatus(SKYPE4COMLib.Call pCall, SKYPE4COMLib.TCallStatus Status)
		{
			//save reference to call for futher call finishing 
			call = pCall;

			//if connection success
			if (Status == SKYPE4COMLib.TCallStatus.clsInProgress)
			{
				if (m_currentCall != null)
					m_currentCall = null;

				m_currentCall = pCall;

				//begin file playing
				pCall.set_InputDevice(SKYPE4COMLib.TCallIoDeviceType.callIoDeviceTypeFile, @"d:\test.wav");
				//Run timer
				timer.Start();
			}
		}

		void Skype_MessageStatus(SKYPE4COMLib.ChatMessage pMessage, SKYPE4COMLib.TChatMessageStatus Status)
		{
			if (Status == SKYPE4COMLib.TChatMessageStatus.cmsReceived && this.m_collection.ContainsByDisplayNameOrHandle(pMessage.FromDisplayName, pMessage.FromHandle))
			{
				string translate = null;
				System.Threading.Tasks.Task taskTranslate = new System.Threading.Tasks.Task(new Action(() =>
					{
						Translator.GoogleTranslateApi.GoogleTranslateApiImplementation.GoogleTranslateResponse objectModel = m_translateModule.TranslateGoogleObject(pMessage.Body);
						if (objectModel != null && objectModel.sentences != null && objectModel.sentences.Count > 0)
						{
							StringBuilder sb = new StringBuilder();
							foreach (var item in objectModel.sentences)
							{
								sb.Append(item.trans);
							}

							translate = sb.ToString();
							sb.Clear();
							bool empty = false;

							TabItem selItem = null;
							TextBox txtMsg = null;

							mainTabCtrl.Dispatcher.Invoke(new Action(() =>
							{
								selItem = mainTabCtrl.Items.Cast<TabItem>().FirstOrDefault(
									x => string.Compare(((string)x.Header), pMessage.FromDisplayName) == 0);
								if (selItem != null)
								{
									txtMsg = selItem.Content as TextBox;
								}
							}
							), System.Windows.Threading.DispatcherPriority.Normal);

							if (selItem == null)
							{
								txtMsg = CreateNewTab(pMessage.FromDisplayName);
							}

							txtMsg.Dispatcher.Invoke(new Action(() => empty = string.IsNullOrEmpty(txtMsg.Text)), System.Windows.Threading.DispatcherPriority.Normal);
							string fullMessage = null;
							if (empty)
								fullMessage = string.Format("{0} \t From:{1} {2} [{5}] {2} {3} {2} {4}", pMessage.Timestamp.ToString("dd.MM.yy hh:mm:ss"), pMessage.FromDisplayName, Environment.NewLine, translate, c_separator, pMessage.Body);
							else
								fullMessage = string.Format("{2}{0} \t From:{1} {2} [{5}] {2} {3} {2} {4}", pMessage.Timestamp.ToString("dd.MM.yy hh:mm:ss"), pMessage.FromDisplayName, Environment.NewLine, translate, c_separator, pMessage.Body);

							txtMsg.Dispatcher.Invoke(new Action(() => txtMsg.AppendText(fullMessage)), System.Windows.Threading.DispatcherPriority.Normal);
						}
					}));
				taskTranslate.Start();

				if (System.Configuration.ConfigurationManager.AppSettings["MakeCallWithTranslation"] == "true")
				{
					taskTranslate.ContinueWith(task =>
					{
						//Get voices of translation
						byte[] bytes = GetFile(translate, m_translateModule.GetTranslateLanguage());

						System.IO.Stream stream = new System.IO.MemoryStream(bytes);
						//Because Skype4COM can work only with wav files, recode mp3 into wav.
						TimeSpan time = Mp3ToWav(stream, @"d:\test.wav");

						//Звоним абоненту
						App.Skype.PlaceCall(pMessage.FromHandle);

						//И отправляем данному абоненту перевод
						pMessage.Chat.SendMessage(translate);

						//Задаём, через какое время нужно повесить трубку
						timer = new System.Timers.Timer(time.TotalMilliseconds);
						timer.Elapsed += FinishCall;
						timer.AutoReset = false;
					}, System.Threading.Tasks.TaskContinuationOptions.OnlyOnRanToCompletion);
				}

				taskTranslate.ContinueWith(task =>
				{
					App.Log.Error(task.Exception.Message, task.Exception);
				}, System.Threading.Tasks.TaskContinuationOptions.NotOnRanToCompletion);
			}
		}

		internal string GetTranslate(string message, string toLang)
		{
			//Create object for futher work with web-request 
			using (xNet.Net.HttpRequest request = new xNet.Net.HttpRequest())
			{
				xNet.Collections.StringDictionary reqParams = new xNet.Collections.StringDictionary();

				string myLang;
				if (toLang == "en")
				{
					myLang = "ru";
				}
				else
				{
					myLang = "en";
				}

				//Set request parameters
				request.UserAgent = xNet.Net.HttpHelper.RandomChromeUserAgent();

				reqParams["text"] = message;
				reqParams["tl"] = toLang;
				reqParams["sl"] = myLang;
				reqParams["client"] = "x";

				//Get response from the server
				string s = request.Get("http://translate.google.ru/translate_a/t", reqParams).ToText();

				//Get translation from response

				int index1 = s.IndexOf(":\"");
				int index2 = s.IndexOf("\"", index1);
				string translate = s.Substring(index1, index2);

				return translate;
			}
		}

		internal byte[] GetFile(string translate, string toLang)
		{
			using (xNet.Net.HttpRequest request = new xNet.Net.HttpRequest())
			{
				xNet.Collections.StringDictionary reqParams = new xNet.Collections.StringDictionary();

				//Задаём необходимые параметры веб-запроса
				reqParams["ie"] = "UTF-8";
				reqParams["q"] = translate;
				reqParams["tl"] = toLang;
				reqParams["prev"] = "input";

				//Получаем файл
				byte[] bytes = request.Get("http://translate.google.ru/translate_tts", reqParams).ToBytes();

				return bytes;
			}
		}

		internal static TimeSpan Mp3ToWav(System.IO.Stream mp3File, string outputFile)
		{
			//Создаём объект для чтения mp3 файла
			using (NAudio.Wave.Mp3FileReader reader = new NAudio.Wave.Mp3FileReader(mp3File))
			{
				//Задаём формат выходного файла
				var newFormat = new NAudio.Wave.WaveFormat(16000, 16, 1);
				using (NAudio.Wave.WaveStream pcmStream = new NAudio.Wave.WaveFormatConversionStream(newFormat, reader))
				{
					//Записываем перекодированный поток в файл
					NAudio.Wave.WaveFileWriter.CreateWaveFile(outputFile, pcmStream);

					//Возвращаем продолжительность файла
					return reader.TotalTime;
				}
			}
		}

		private void FinishCall(object sender, System.Timers.ElapsedEventArgs e)
		{
			call.Finish();
		}
	}
}
